package com.tx.core.mybatis.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.tree.model.TreeEntity;
import org.apache.commons.collections4.CollectionUtils;

import java.io.Serializable;
import java.util.*;

/**
 * <pre>
 * 功能简述: 基础业务层接口定义
 * </pre>
 *
 * @author PengQingyang
 * 创建时间 [2024/3/31]
 */
public interface ITreeEntityService<ID extends Serializable, T extends TreeEntity<ID, T>> {

    /**
     * 获取 entity 的 class
     *
     * @return {@link Class<T>}
     */
    Class<T> getEntityClass();

    /**
     * 查询列表的通用方法，根据提供的参数映射进行查询，并返回查询结果列表。
     *
     * @param params 用于查询的参数映射，键值对形式，具体依赖于实际查询需求。
     * @return 返回查询结果的列表，列表元素类型为泛型 T。
     */
    List<T> queryList(Map<String, Object> params);

    /**
     * 查询指定父级ID下的子级列表
     *
     * @param parentId 父级的ID，不能为空
     * @param params   查询参数，可以为空，为空时将新建一个空的参数映射
     * @return 子级列表，返回类型为泛型列表
     */
    default List<T> queryChildrenByParentId(ID parentId, Map<String, Object> params) {
        // 确保parentId不为空
        AssertUtils.notEmpty("parentId", "parentId is empty.");

        // 如果查询参数为空，则新建一个参数映射，否则使用传入的参数映射
        params = params == null ? new HashMap<String, Object>() : params;
        // 添加父级ID作为查询条件
        params.put("parentId", parentId);
        // 执行查询，并返回结果列表
        return queryList(params);
    }

    /**
     * 查询给定父级ID下的子孙级列表
     *
     * @param parentId 父级ID，标识查询的起点
     * @param params   查询参数，可包含各种过滤条件
     * @return 子孙级列表，返回的结果列表包含给定父级ID下的所有子孙级项
     */
    default List<T> queryDescendantsByParentId(ID parentId, Map<String, Object> params) {
        // 确保parentId不为空
        AssertUtils.notEmpty("parentId", "parentId is empty.");

        // 如果params为空，则初始化一个新的空HashMap，确保后续操作不会因为params为空而出错
        params = params == null ? new HashMap<>() : params;
        // 用于存储查询结果的ID集合
        Set<ID> ids = new HashSet<>();
        // 初始化父ID集合，加入起始父ID
        Set<ID> parentIds = new HashSet<>();
        parentIds.add(parentId);

        // 执行嵌套查询，并将结果临时存储在resListTemp中
        return doNestedQueryList(ids, parentIds, params);
    }

    /**
     * 嵌套查询子孙级列表
     * 递归查询所有给定父级ID的子孙级项，并返回一个列表。
     *
     * @param ids       已查询过的ID集合，用于避免重复查询和循环查询。
     * @param parentIds 待查询的父级ID集合。
     * @param params    查询参数，用于构造查询条件。
     * @return 返回包含所有符合条件的子孙级项的列表。
     */
    private List<T> doNestedQueryList(Set<ID> ids,
                                      Set<ID> parentIds, Map<String, Object> params) {
        if (CollectionUtils.isEmpty(parentIds)) {
            return new ArrayList<T>(); // 如果父级ID集合为空，则直接返回空列表
        }
        // 构造查询参数，添加父级ID集合作为查询条件
        Map<String, Object> newQueryParams = new HashMap<>();
        newQueryParams.putAll(params);
        newQueryParams.put("parentIds", parentIds);
        List<T> resList = queryList(newQueryParams); // 执行查询

        Set<ID> newParentIds = new HashSet<>();
        for (T cpTemp : resList) {
            if (!ids.contains(cpTemp.getId())) {
                newParentIds.add(cpTemp.getId()); // 将新发现的父级ID添加到集合中，用于下一次递归查询
            }
            // 为了避免循环查询，将已查询过的ID添加到ids集合中
            ids.add(cpTemp.getId());
        }
        // 递归调用，查询下一层级的子孙级项，并将结果添加到当前列表中
        resList.addAll(doNestedQueryList(ids, newParentIds, params));
        return resList; // 返回包含所有子孙级项的列表
    }

    /**
     * 查询列表。该方法用于根据提供的条件查询列表数据。
     *
     * @param wrapper 查询条件包装对象，用于指定查询的详细条件。
     * @param params  一个包含额外参数的Map，可用于定制查询行为或传递给查询过程。
     * @return 返回一个T类型的列表，包含所有匹配查询条件的结果。
     */
    List<T> queryList(Wrapper<T> wrapper, Map<String, Object> params);

    /**
     * 查询指定父级ID下的子级列表
     *
     * @param parentId 父级的ID，不能为空
     * @param wrapper  查询条件包装对象，用于指定更详细的查询条件
     * @return 返回符合查询条件的子级列表
     */
    default List<T> queryChildrenByParentId(ID parentId, Wrapper<T> wrapper) {
        // 确保parentId不为空
        return queryChildrenByParentId(parentId, wrapper, null);
    }

    /**
     * 查询指定父级ID下的子级列表
     *
     * @param parentId 父级的ID，不能为空
     * @param wrapper  查询条件包装对象，用于指定更详细的查询条件
     * @param params   查询参数映射，可包含额外的查询参数；如果为空，则会创建一个新映射并添加parentId作为查询条件
     * @return 返回符合查询条件的子级列表
     */
    default List<T> queryChildrenByParentId(ID parentId, Wrapper<T> wrapper, Map<String, Object> params) {
        // 确保parentId不为空
        AssertUtils.notEmpty("parentId", "parentId is empty.");

        // 初始化或更新查询参数映射，并添加parentId作为查询条件
        params = params == null ? new HashMap<>() : params;
        params.put("parentId", parentId);
        // 执行查询并返回结果列表
        return queryList(wrapper, params);
    }

    /**
     * 根据父级ID查询其所有后代元素列表。
     *
     * @param parentId 父级元素的ID，标识要查询的父级元素。
     * @param wapper   查询条件包装器，用于指定查询的详细条件。
     * @return 返回符合查询条件的后代元素列表。
     */
    default List<T> queryDescendantsByParentId(ID parentId, Wrapper<T> wapper) {
        return queryDescendantsByParentId(parentId, wapper, null);
    }

    /**
     * 查询给定父ID的子孙级列表
     *
     * @param parentId 父级ID，用于指定查询的起始层级
     * @param params   附加参数，可用于定制查询行为，可选
     * @return 返回满足条件的子孙级列表
     */
    default List<T> queryDescendantsByParentId(ID parentId, Wrapper<T> wapper, Map<String, Object> params) {
        // 确保父ID不为空
        AssertUtils.notEmpty("parentId", "parentId is empty.");

        // 处理入参params，如果为空则新建一个HashMap
        params = params == null ? new HashMap<>() : params;
        // 用于存储查询结果的ID集合
        Set<ID> ids = new HashSet<>();
        // 初始化父ID集合，加入起始父ID
        Set<ID> parentIds = new HashSet<>();
        parentIds.add(parentId);

        // 执行嵌套查询，将结果存储在resListTemp中
        List<T> resListTemp = doNestedQueryList(ids, parentIds, params);
        return resListTemp;
    }

    /**
     * 执行嵌套查询列表
     *
     * @param ids       当前层级的实体ID集合
     * @param parentIds 父级实体ID集合
     * @param wapper    查询包装器
     * @param params    查询参数
     * @return 返回包含当前及所有子级实体的列表
     */
    private List<T> doNestedQueryList(Set<ID> ids,
                                      Set<ID> parentIds, Wrapper<T> wapper, Map<String, Object> params) {
        if (CollectionUtils.isEmpty(parentIds)) {
            return new ArrayList<T>(); // 如果父级ID集合为空，则直接返回空列表
        }
        //为了避免因数据错误导致无限循环，对查询参数进行处理
        Map<String, Object> newQueryParams = new HashMap<>();
        newQueryParams.putAll(params);
        newQueryParams.put("parentIds", parentIds);
        List<T> resList = queryList(wapper, newQueryParams); // 执行查询

        Set<ID> newParentIds = new HashSet<>();
        for (T cpTemp : resList) {
            if (!ids.contains(cpTemp.getId())) {
                newParentIds.add(cpTemp.getId()); // 将查询结果中不在当前IDs集合中的ID，加入到新的父级ID集合中
            }
            //为防止循环查询，将查询过的ID加入到当前IDs集合中
            ids.add(cpTemp.getId());
        }
        //递归调用，查询下一层级的子实体
        resList.addAll(doNestedQueryList(ids, newParentIds, params));
        return resList; // 返回包含所有层级实体的列表
    }

}
